package com.interview.javabasic.Algorithm.SnowFlake;

/*
@李子宁
Happy,happy everyday!
冲鸭！
2021/3/2

*/

import java.sql.Time;

/*
* 雪花算法用于生成分布式ID：结构图：一共64位
* 算法描述：
*   1、最高位是符号位，始终为0，不可用
*   2、41位时间序列，精确到毫秒级别，41位的长度可以使用69年。时间位还有一个很重要的作用就是可以根据时间进行排序
*   3、 10位的机器标识，10位的长度最多支持部署1024个节点
*   4、 12位的技术序列号，序列号即一系列的自增id，可以支持同一节点同一毫秒生成多个ID序号，12位的技术序列号支持每个节点每毫秒产生4069个ID序号
* */
public class SnowFlakeDemo {
    //起始时间戳
    private final static long tpoch = 1557825652094L;

    //每一部分占用的位数
    private final static long workerIdBits = 5L;
    private final static long datacenterIdBits = 5L;
    private final static long sequenceBits = 12L;

    //每一部分的最大值
    private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);
    private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    private final static long maxSequence = -1L ^ (-1L << sequenceBits);

    //每一部分向左的位移
    private final static long workerIdShift = sequenceBits;
    private final static long datacenterIdShift = sequenceBits + workerIdBits;
    private final static long timestampShift = sequenceBits + workerIdBits;

    private long datacenterId;//数据中心ID
    private long workerId;//机器ID
    private long sequence = 0L;//序列号
    private long lastTimeStamp = -1L;//上一次的时间戳

    public SnowFlakeDemo(long workerId, long datacenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(
                    String.format("机器ID不能大于 %d 或小于0", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0){
            throw new IllegalArgumentException(
                    String.format("数据中心ID不能大于 %d 或小于 0", maxDatacenterId));
        }

        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }

    public synchronized long nextId() {
        long timestamp = System.currentTimeMillis();
        if (timestamp < lastTimeStamp) {
            throw new RuntimeException(
                    String.format(
                            "Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimeStamp - timestamp)
            );
        }
        if (timestamp == lastTimeStamp) {
            sequence = (sequence + 1) & maxSequence;
            if (sequence == 0L) {
                timestamp = tilNextMillis();
            }
        } else {
            sequence = 0L;
        }
        lastTimeStamp = timestamp;

        return (timestamp - tpoch) << timestampShift//时间戳部分
                | datacenterId << datacenterIdShift//数据中心部分
                | workerId << workerIdShift//机器标志部分
                |sequence;//序号部分
    }

    private long tilNextMillis() {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimeStamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }

    public static void main(String[] args) {
        SnowFlakeDemo demo = new SnowFlakeDemo(10,12);
        System.out.println(demo.nextId());
        System.out.println(demo.nextId());
        System.out.println(demo.nextId());
    }
}
